<?php

/**
 * This is the model class for table "user_oauth".
 *
 * The followings are the available columns in table 'user_oauth':
 * @property integer $user_id
 * @property string $provider name of provider
 * @property string $identifier unique user authentication id that was returned by provider
 * @property string $profile_cache
 * @property string $session_data session data with user profile
 *
 *
 * @version 1.2.4
 * @copyright Copyright &copy; 2013 Sviatoslav Danylenko
 * @author Sviatoslav Danylenko <dev@udf.su> 
 * @license MIT ({@link http://opensource.org/licenses/MIT})
 * @link https://github.com/SleepWalker/hoauth
 */
class UserOAuth extends CActiveRecord
{
	/**
	 * @var $_hybridauth HybridAuth class instance
	 */
	protected $_hybridauth;

	/**
	 * @var $_adapter HybridAuth adapter	
	 */
	protected $_adapter;

	/**
	 * @var $_profileCache property for holding of unserialized 
	 *		profile cache copy
	 */
	protected $_profileCache;

	/**
	 * Returns the static model of the specified AR class.
	 * @param string $className active record class name.
	 * @return UserOAuth the static model class
	 */
	public static function model($className=__CLASS__)
	{
		try
		{
			$model = parent::model($className);

			// db updates 'on the fly'
			$model->updateDb($model);

			return $model;
		}
		catch(CDbException $e)
		{
			self::createDbTable();
			Yii::app()->controller->refresh();
		}
	}

	/**
	 * @return string the associated database table name
	 */
	public function tableName()
	{
		if(!empty(Yii::app()->db->tablePrefix))
			return '{{user_oauth}}';
		else
			return 'user_oauth';
	}

	/**
	 * @return array validation rules for model attributes.
	 */
	public function rules()
	{
		return array();
	}

	public function afterFind()
	{
		parent::afterFind();

		if(!empty($this->profile_cache))
			$this->_profileCache = (object)unserialize($this->profile_cache);
	}

	public function beforeSave() 
	{
		if(!empty($this->_profileCache))
			$this->profile_cache = serialize((array)$this->_profileCache);

		return parent::beforeSave();
	}

	/**
	 * @static
	 * @access public
	 * @return configuration array of HybridAuth lib
	 */
	public static function getConfig()
	{
		$config = self::getConfigPath();

		if(!file_exists($config))
		{
			$oldConfig = dirname(__FILE__) . '/../hybridauth' . '/config.php';

			if(file_exists($oldConfig))
			{
				// TODO: delete this in next versions
				if (is_writable($yiipath) && is_writable($oldConfig)) // trying to move old config to the new dir
				rename($oldConfig, $config);
				else
					$config = $oldConfig;
			}
			else
				throw new CException("The config.php file doesn't exists");
		}

		return require($config);
	}

	/**
	 * @return path to the HybridAuth config file
	 */
	public static function getConfigPath()
	{
		$config = Yii::app()->params['hoauth']['configAlias'];
		if(empty($config))
		{
			$yiipath = Yii::getPathOfAlias('application.config.hoauth');
		}
		$config = $yiipath . '.php';

		return $config;
	}
	
	/**
	 * @access public
	 * @return array of UserOAuth models
	 */
	public function findUser($user_id, $provider = false)
	{
		$params = array('user_id' => $user_id);
		if($provider)
		{
			$params['provider'] = $provider;
			return $this->findByAttributes($params);
		}
		else
			return $this->findAllByAttributes($params);
	}

	/**
	 * @access public
	 * @return Auth class. With restored users authentication session data
	 * @link http://hybridauth.sourceforge.net/userguide.html
	 * @link http://hybridauth.sourceforge.net/userguide/HybridAuth_Sessions.html
	 */
	public function getHybridAuth()
	{
		if(!isset($this->_hybridauth))
		{
			$path = dirname(__FILE__) . '/../hybridauth';

			require_once($path.'/Hybrid/Auth.php');
			$this->_hybridauth = new Hybrid_Auth( self::getConfig() );

			if(!empty($this->session_data))
				$this->_hybridauth->restoreSessionData($this->session_data);
		}

		return $this->_hybridauth;
	}

	/**
	 * @access public
	 * @return Adapter for current provider or null, when we have no session data.
	 * @link http://hybridauth.sourceforge.net/userguide.html
	 */
	public function getAdapter()
	{
		if(!isset($this->_adapter) && isset($this->session_data) && isset($this->provider))
			$this->_adapter = $this->hybridAuth->getAdapter($this->provider);

		return $this->_adapter;
	}

	/**
	 * authenticates user by specified adapter	
	 * 
	 * @param string $provider 
	 * @access public
	 * @return void
	 */
	public function authenticate($provider)
	{
		if(empty($this->provider))
		{
			try
			{
				$this->_adapter = $this->hybridauth->authenticate($provider);
				$this->identifier = $this->profile->identifier;
				$this->provider = $provider;
				$oAuth = self::model()->findByPk(array('provider' => $this->provider, 'identifier' => $this->identifier));
				if($oAuth)
					$this->setAttributes($oAuth->attributes, false);
				else
					$this->isNewRecord = true;

				$this->session_data = $this->hybridauth->getSessionData();
				return $this;
			}
			catch( Exception $e )
			{
				$error = "";
				switch( $e->getCode() )
				{ 
					case 6 : //$error = "User profile request failed. Most likely the user is not connected to the provider and he should to authenticate again."; 
					case 7 : //$error = "User not connected to the provider."; 
					$this->logout();
					return $this->authenticate($provider);
					break;
				}
			}
		}

		return null;
	}

	/**
	 * Breaks HybridAuth session and logs user from sn out.
	 *
	 * @access public
	 */
	public function logout()
	{
		if(!empty($this->_adapter))
		{
			$this->_adapter->logout();
			$this->unsetAttributes(); 
		}
	}

	/**
	 * @access public
	 * @return Hybrid_User_Profile user social profile object
	 */
	public function getProfile()
	{
		$profile = $this->adapter->getUserProfile();
		//caching profile
		$this->_profileCache = $profile;

		return $profile;
	}

	/**
	 * binds local user to current provider 
	 * 
	 * @param mixed $user_id id of the user
	 * @access public
	 * @return whether the model successfully saved
	 */
	public function bindTo($user_id)
	{
		$this->user_id = $user_id;
		return $this->save();
	}

	/**
	 * @access public
	 * @return whether this social network account bond to existing local account
	 */
	public function getIsBond()
	{
		return !empty($this->user_id);
	}

	/**
	 * Getter for cached profile.
	 * We implement this method, because in older version of hoauth was no profile cache. So we need to fill db with caches
	 * The second reason is camelCase
	 */
	public function getProfileCache()
	{
		if(empty($this->_profileCache))
		{
			$this->getProfile();
			$this->save();
		}

		return $this->_profileCache;
	}

	/**
	 * creates table for holding provider bindings	
	 */
	protected static function createDbTable()
	{
		//TODO: remove me in newer versions
		if(Yii::app()->db->getSchema()->getTable('user_oauth') !== null && !empty(Yii::app()->db->tablePrefix))
		{
			// providing table rename, to handle support of prefixed tables in v.1.2.2
			Yii::app()->db->createCommand('RENAME TABLE `user_oauth` TO `tbl_user_oauth`')->execute();
			Yii::app()->controller->refresh();
		}

		$sql = file_get_contents(dirname(__FILE__).'/user_oauth.sql');
		$sql = strtr($sql, array('{{user_oauth}}' => Yii::app()->db->tablePrefix . 'user_oauth'));
		Yii::app()->db->createCommand($sql)->execute();
	}

	/**
	 * Runs DB updates on the fly
	 */
	protected function updateDb($model)
	{
		$updates = array();
		// the try statement to correct my stupid column names in v1.0.1 of hoauth
		// sory about this
		try
		{
			$model->provider=$model->provider;
		}
		catch(Exception $e)
		{
			ob_start();
			?>
			ALTER TABLE <?php echo '`' . $model->tableName() . '`'; ?> CHANGE `name` `provider` VARCHAR( 45 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
			CHANGE `value` `identifier` VARCHAR( 64 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL
			<?php
			$updates[] = ob_get_clean();
		}

		// profile caching since v.1.2.2
		try
		{
			$model->profile_cache=$model->profile_cache;
		}
		catch(Exception $e)
		{
			ob_start();
			?>
			ALTER TABLE <?php echo '`' . $model->tableName() . '`'; ?> ADD `profile_cache` TEXT NOT NULL AFTER `identifier`
			<?php
			$updates[] = ob_get_clean();
		}

		if(count($updates))
		{
			foreach($updates as $sql)
			{
				Yii::app()->db->createCommand($sql)->execute();
			}
			Yii::app()->controller->refresh();
		}
	}

	/**
	 * @return array relational rules.
	 */
	public function relations()
	{
		// NOTE: you may need to adjust the relation name and the related
		// class name for the relations automatically generated below.
		return array(
			'user' => array(self::BELONGS_TO, 'User', 'user_id'),
		);
	}
}
